// id="app" id='app' id=app
const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/
// 标签名 <my-header></my-header>
const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z]*`
// <my:header></my:header>
const qnameCapture = `((?:${ncname}\\:)?${ncname})`
// <div
const startTagOpen = new RegExp(`^<${qnameCapture}`)
// > />
const startTagClose = /^\s*(\/?)>/
// </div>
const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`)

// 解析模板并且编译成AST语法树
export function parseHTML (html) {
  const ELEMENT_TAG_TYPE = 1
  const TEXT_TAG_TYPE = 3
  let text,
      root,
      currentParent,
      stack = []
  
  while(html) {
    // textEnd 为 0 说明是一个开始或者结束标签
    // textEnd 不等于 0 说明是文本
    let textEnd = html.indexOf('<')

    if (textEnd === 0) {
      const startTagMatch = parseStartTag()
      if (startTagMatch) {
        start(startTagMatch.tagName, startTagMatch.attrs)
        continue
      }

      const endTagMatch = html.match(endTag)
      if (endTagMatch) {
        advance(endTagMatch[0].length)
        end(endTagMatch[1])
        continue
      }
    }

    if (textEnd > 0) {
      text = html.substring(0, textEnd)
    }

    if (text) {
      advance(text.length)
      chart(text)
    }
  }

  // 字符串截取
  function advance (n) {
    html = html.substring(n)
  }

  function parseStartTag () {
    const start = html.match(startTagOpen)
    if (start) {
      const match = {
        tagName: start[1],
        attrs: []
      }
      advance(start[0].length)

      let end,
          attr

      // 当没有匹配到结束标签（> />），但是匹配到了属性时
      while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) {
        match.attrs.push({
          name: attr[1],
          value: attr[3]
        })
        advance(attr[0].length)
      }

      if (end) {
        advance(end[0].length)
        return match
      }
    }
  }

  function start (tagName, attrs) {
    const element = createASTElement(tagName, attrs)

    if (!root) {
      root = element
    }

    currentParent = element
    stack.push(element)
  }

  function end () {
    const element = stack.pop()
    currentParent = stack[stack.length - 1]

    if (currentParent) {
      element.parent = currentParent
      currentParent.children.push(element)
    }
  }

  function chart (text) {
    text = text.trim()
    if (text.length > 0) {
      currentParent.children.push({
        type: TEXT_TAG_TYPE,
        text
      })
    }
  }

  function createASTElement (tagName, attrs) {
    return {
      tag: tagName,
      type: ELEMENT_TAG_TYPE,
      children: [],
      attrs,
      parent: null
    }
  }

  return root
}